package org.codetranslators.compiler.intermrep;

import java.util.Vector;

import org.codetranslators.common.Temp;
import org.codetranslators.compiler.abstractassembly.AssemCJump;

public class CJump  implements AssemblyInstruction
{
	
	public final static int EQ = 0;
	public final static int NE = 1;
	public final static int LT = 2;
	public final static int GT = 3;
	public final static int LE = 4;
	public final static int GE = 5;
	public final static int ULT = 6;
	public final static int ULE = 7;
	public final static int UGT = 8;
	public final static int UGE = 9;
	
	// TODO -> What are ULT, ULE, UGT, UGE? They are not being handled in the code..
	
	private int relop;
	
	private String instructionDescription;
	private Vector srcList;
	private String ifLabel;
	private String elseLabel;
	
	private Integer immediateValue;
	
	public CJump(Vector srcList, String ifLabel, String falseLabel, int relop,
				 Integer immediateValue)
	{
		this.srcList = srcList;
		this.ifLabel = ifLabel;
		this.elseLabel = falseLabel;
		this.relop = relop;
		this.immediateValue = immediateValue;
		generateMipsStyleCode();
	}
	
	private void generateMipsStyleCode(){
		if(immediateValue != null){
			// Comparison with a constant value
			int value = immediateValue.intValue();
			Temp temp = (Temp) srcList.elementAt(0);   // Only one temp in the statement
			instructionDescription = getCJumpString(relop, temp, value);
		}
		else
		{
			// Comparison with two temporaries
			Temp lhsTemp = (Temp) srcList.elementAt(0);
			Temp rhsTemp = (Temp) srcList.elementAt(1);
			instructionDescription = getCJumpString(relop, lhsTemp.toString(), rhsTemp.toString());
		}
	}
	
	private String getCJumpString(int op, Temp src1, int value)
	{
		String branchOnStr = "";
		if(op == AssemCJump.EQ || op == AssemCJump.NE)
		{
			if(value == 0)
			{
				if(op == AssemCJump.EQ) 
					branchOnStr = tab + "beqz " + src1.toString() + ", " + 
					ifLabel + newLine;
				else 
					branchOnStr = tab + "bnez " + src1.toString() + ", " 
					+ ifLabel + newLine;
			}
			else 
			{
				branchOnStr = getCJumpString(op, src1.toString(), ""+value);
			}
		}
		else
		{
			if(value == 0)
			{
				if(op == AssemCJump.GT)
					branchOnStr = tab + "bgtz " + src1.toString() + ", " + 
					ifLabel.toString() + newLine;
				else if(op == AssemCJump.GE)
					branchOnStr = tab + "bgez " + src1.toString() + ", " + 
					ifLabel.toString() + newLine;
				else if(op == AssemCJump.LT)
					branchOnStr = tab + "bltz " + src1.toString() + ", " + 
					ifLabel.toString() + newLine;
				else 
					branchOnStr = tab + "blez " + src1.toString() + ", " + 
					ifLabel.toString() + newLine;
			}
			else
			{
				branchOnStr = getCJumpString(op, src1.toString(), ""+value);
			}
		}
		return branchOnStr;
	}
	
	private String getCJumpString(int op, String src1, String src2)
	{
		String branchOnStr = "";
		if(op == AssemCJump.EQ) 
			branchOnStr = tab + "beq " + src1.toString() + ", "  
					+ src2 + ", " + ifLabel + newLine;
		else if(op == AssemCJump.NE)
			branchOnStr = tab + "bne " + src1.toString() + ", " 
			+ src2 + ", " +  ifLabel + newLine;
		else if(op == AssemCJump.GT)
			branchOnStr = tab + "bgt " + src1.toString() + ", "  
			+ src2 + ", " + ifLabel + newLine;
		else if(op == AssemCJump.GE)
			branchOnStr = tab + "bge " + src1.toString() + ", "  
			+ src2 + ", " + ifLabel + newLine;
		else if(op == AssemCJump.LT)
			branchOnStr = tab + "blt " + src1.toString() + ", "  
			+ src2 + ", " + ifLabel + newLine;
		else 
			branchOnStr = tab + "ble " + src1.toString() + ", "  
			+ src2 + ", " + ifLabel + newLine;
		
		return branchOnStr;
	}
	
	/**
	 * Reverses the condition while preserving the semantics. 
	 * For example, "bgt $t0, $t1, L5 (else L6) " 
	 * will be converted to "
	 */
	public void setConverse(){
		
		int newRelop = -1;
		if(relop == AssemCJump.EQ){
			newRelop = AssemCJump.NE;
		}
		else if(relop == AssemCJump.NE){
			newRelop = AssemCJump.EQ;
		}
		else if(relop == AssemCJump.LT){
			newRelop = AssemCJump.GE;
		}
		else if(relop == AssemCJump.GT){
			newRelop = AssemCJump.LE;
		}
		else if(relop == AssemCJump.LE){
			newRelop = AssemCJump.GT;
		}
		else if(relop == AssemCJump.GE){
			newRelop = AssemCJump.LT;
		}
	
		relop = newRelop;
		
		// If this instruction has two temps as source, swap the if-else labels
		String temp = ifLabel;
		ifLabel = elseLabel;
		elseLabel = temp;

		
		// Regenerate instruction
		generateMipsStyleCode();
		
	}
	
	public String getAssemCode(boolean showUseDef)
	{
		if(!showUseDef)
			return instructionDescription;
		else
		{
			String comment = "				# Use = ";
			
			if(srcList == null) comment += "None";
			else
			{
				int numSrcTemps = srcList.size();
				for(int i = 0; i < numSrcTemps; i++)
				{
					comment += (Temp) srcList.elementAt(i) + " ";
				}
			}
			
			// Remove the newline at the end of assemcode
			int nlOffset = instructionDescription.lastIndexOf("\n");
			if(nlOffset > 0)
			{
				instructionDescription = instructionDescription.substring(0, nlOffset);
			}
			
			return instructionDescription + comment;
		}
	}
	
	public Vector getSrcList()
	{
		return srcList;
	}
	
	public Vector getDestList()
	{
		return null;
	}
	
	public Vector getJumps()
	{
		Vector lblList = new Vector();
		lblList.addElement(ifLabel);
		lblList.addElement(elseLabel);
		return lblList;
	}
	
	public String getLabelStr()
	{
		return null;
	}
	
	public int getType()
	{
		return AssemblyInstruction.CJUMP;
	}
	
	public boolean isMove()
	{
		return false;
	}
	
	public String getTrueLabel(){ return ifLabel; }
	public String getFalseLabel(){ return elseLabel; }
	public int getRelOp(){ return relop;}
	public Integer getImmediateValue(){ return immediateValue;}
}
